package main.annotation;

import java.util.HashMap;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import parser.Symbol;
import parser.sym;

public abstract class InterfaceAnnotation {
	// Annotation types
	public static final int PARAMETER_ANNOTATION = 1;
	public static final int CHART_ANNOTATION = 2;
	public static final int DATA_ANNOTATION = 3;
	public static final int DATALAYER_ANNOTATION = 4;
	public static final int DATASET_ANNOTATION = 5;
	public static final int EXTERNAL_ANNOTATION = 6;
	public static final int EXTERNAL_VARIABLE_ANNOTATION = 7;
	public static final int NOSTEP_ANNOTATION = 8;
	public static final int OBSERVER_ANNOTATION = 9;
	public static final int STEP_ANNOTATION = 10;
	public static final int TICK_ANNOTATION = 11;


	// Annotation type
	private final int annotationType;
	
	/* Indicates whether the annotation was generated automatically */
	private boolean autoGenerated;

	/* Annotation elements (items) */
	// element.name and key can be different, that is,
	// key represents SparkLogo name meanwhile element.name
	// represents xml model description name
	protected final HashMap<String, AnnotationElement> items;

	
	/**
	 * Protected constructor
	 * @param id
	 */
	protected InterfaceAnnotation(int type) {
		this.items = new HashMap<String, AnnotationElement>();
		this.annotationType = type;
	}
	
	/**
	 * Returns the identifier of the annotation
	 * @return
	 */
	public int getType() {
		return annotationType;
	}
	
	
	/**
	 * Returns true if the annotation was generated automatically
	 * @return
	 */
	public boolean isAutoGenerated() {
		return autoGenerated;
	}
	
	
	/**
	 * Sets autoGenerated flag
	 * @param autoGenerated
	 */
	public void setAutoGenerated(boolean autoGenerated) {
		this.autoGenerated = autoGenerated;
	}
	
	
	/**
	 * Begins annotation parsing
	 * @param s
	 * @return
	 */
	public static InterfaceAnnotation beginParsing(Symbol s) throws Exception {
		if (s.id != sym.IDENTIFIER)
			throw new Exception("An identifier is expected: " + s);
		
		String name = (String) s.value;
		if (name.equals("chart"))
			return new ChartAnnotation();
		
		if (name.equals("parameter"))
			return new ParameterAnnotation();
		
		if (name.equals("datalayer"))
			return new DataLayerAnnotation();

		if (name.equals("dataset"))
			return new DatasetAnnotation();
		
		if (name.equals("data"))
			return new DataAnnotation();
		
		if (name.equals("nostep"))
			return new NoStepAnnotation();
		
		if (name.equals("step"))
			return new StepAnnotation();
		
		if (name.equals("external"))
			return new ExternalAnnotation();
		
		if (name.equals("tick"))
			return new TickAnnotation();
		
		if (name.equals("observer"))
			return new ObserverAnnotation();
		
		throw new Exception("Annotation " + name + " is not defined");
	}
	
	
	/**
	 * Processes an annotation element declaration
	 * @param nameSymbol
	 * @param valueSymbol
	 * @throws Exception
	 */
	public void processElement(Symbol nameSymbol, Symbol valueSymbol) throws Exception {
		if (nameSymbol.id != sym.IDENTIFIER)
			throw new Exception("A name is expected: " + nameSymbol);
		
		AnnotationElement element = items.get((String) nameSymbol.value);
		if (element == null)
			throw new Exception("Annotation element " + nameSymbol.value + " is undefined");
		
		element.parseValue(valueSymbol);
	}
	
	
	/**
	 * Sets special elements
	 */
	public void setGetSetValues(String getName, String setName) {
		if (getName != null) {
			AnnotationElement element = items.get("$get");
			if (element != null)
				element.value = getName;
		}
		
		if (setName != null) {
			AnnotationElement element = items.get("$set");
			if (element != null)
				element.value = setName;
		}
	}
	
	
	@Override
	public String toString() {
		StringBuilder str = new StringBuilder();
		
		for (AnnotationElement element : items.values()) {
			str.append(element.toString());
			str.append(' ');
		}
		
		return str.toString();
	}
	

	/**
	 * Creates an xml node
	 * @param doc
	 * @return
	 */
	public final Node toNode(Document doc, String name) {
		Node node = doc.createElement(name);
		
		for (AnnotationElement element : items.values()) {
			Node attr = element.toAttribute(doc);
			if (attr != null)
				node.getAttributes().setNamedItem(attr);
		}
		
		return node;
	}
	
	
	public abstract Node toNode(Document doc);
}
